// C++完美转发详解
// 完美转发(Perfect Forwarding)是C++11引入的一项重要特性，
// 它允许函数模板将参数"完美"地转发给另一个函数，同时保持原始参数的所有属性，包括值类别(左值/右值)、const限定符等。

// 完美转发的问题背景
// 在C++中，当我们创建一个包装函数(wrapper function)时，通常希望它能够将参数"原封不动"地传递给被包装的函数。
// 然而，这并不容易实现：
// 有问题的包装函数
template <typename T>
void wrapper(T param)
{
    foo(param); // 传递给foo的总是一个左值
}

// 调用
int x = 42;
wrapper(x);            // x是左值，传给wrapper后，param也是左值
wrapper(std::move(x)); // 虽然传入右值，但param仍然是左值

// 这里的问题是，无论传入wrapper的是左值还是右值，param总是一个左值（因为它是一个命名变量）。这意味着我们丢失了参数的原始值类别信息。

// 完美转发的解决方案
// C++11引入了两个关键特性来解决这个问题：
// 1。通用引用(Universal Reference)：T&&在模板参数推导上下文中既可以绑定左值也可以绑定右值
// 2.std::forward：一个条件性转换，只有当原始参数是右值时才将参数转换为右值

// 使用完美转发的包装函数
template <typename T>
void wrapper(T &&param)
{                                // 通用引用
    foo(std::forward<T>(param)); // 保持原始值类别
}

// 完美转发的工作原理
// 完美转发的工作原理基于以下几个关键概念：

// 1. 引用折叠规则
// C++11引入了引用折叠规则，用于解析复杂的引用类型：
// T& & 折叠为 T&
// T&& & 折叠为 T&
// T& && 折叠为 T&
// T&& && 折叠为 T&&
// 简单来说，只要有一个&，结果就是&（左值引用）。

// 2. 模板参数推导规则
// 当使用通用引用时，模板参数推导遵循特殊规则：
// 如果传入左值，T被推导为左值引用类型（如int&）
// 如果传入右值，T被推导为非引用类型（如int）

// 完美转发的实际例子
// 1. 基本示例
#include <iostream>
#include <utility>

void process(int &x)
{
    std::cout << "process(int&): " << x << std::endl;
}

void process(int &&x)
{
    std::cout << "process(int&&): " << x << std::endl;
}

template <typename T>
void forward_to_process(T &&x)
{
    std::cout << "forwarding..." << std::endl;
    process(std::forward<T>(x));
}

int main()
{
    int a = 42;

    process(a);            // 调用 process(int&)
    process(std::move(a)); // 调用 process(int&&)

    forward_to_process(a);            // 转发左值，调用 process(int&)
    forward_to_process(std::move(a)); // 转发右值，调用 process(int&&)

    return 0;
}

// 2. 智能指针工厂函数
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&...args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

class Widget
{
public:
    Widget(int x, std::string &&s)
    {
        // 使用移动语义
    }
};

// 使用
std::string heavy_string = "...";
auto w1 = make_unique<Widget>(42, std::move(heavy_string));
// 完美转发保持了heavy_string的右值性质

// 3. 容器的emplace操作
std::vector<std::unique_ptr<Widget>> widgets;

// 使用完美转发在容器中直接构造元素
widgets.emplace_back(new Widget(42, "hello"));

// emplace_back的简化实现
template <typename... Args>
void emplace_back(Args &&...args)
{
    // 在容器内存中直接构造对象
    new (memory_location) value_type(std::forward<Args>(args)...);
}

// 完美转发的注意事项和限制
// 1. 不能完美转发的情况
// 某些参数类型和表达式不能被完美转发：
//      NULL或0作为空指针：使用nullptr代替
//      仅有声明的静态const整型成员变量
//      位域
//      大多数字符串字面量
//      重载函数的名称（没有显式转换为函数指针）

// 2. 转发引用vs右值引用
// 不要混淆转发引用(T&&)和右值引用(Widget&&)：
template <typename T>
void f(T &&param); // 转发引用，可以接受左值和右值

void f(Widget &&param); // 右值引用，只能接受右值

// 完美转发的性能考虑
// 完美转发是一种零开销抽象，它不会引入额外的运行时开销。然而，它可能会导致代码膨胀，因为编译器可能需要为不同的参数类型组合生成多个函数实例。

// 总结
// 完美转发是C++11引入的强大特性，它允许我们编写通用的包装函数，同时保持原始参数的所有属性。它基于通用引用、引用折叠规则和std::forward的组合使用。

// 完美转发在标准库中得到了广泛应用，如智能指针工厂函数、容器的emplace操作、线程构造等。掌握完美转发是编写高效、通用C++代码的重要技能。